/*
 * Copyright (c) 1997, 2004, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package com.sun.corba.se.impl.oa.poa;

import java.util.Iterator;
import java.util.Collections;
import java.util.Set;
import java.util.HashSet;

import org.omg.CORBA.LocalObject;
import org.omg.CORBA.CompletionStatus;

import org.omg.PortableServer.POAManager;
import org.omg.PortableServer.POAManagerPackage.State;
import org.omg.PortableServer.POA;

import org.omg.PortableInterceptor.DISCARDING;
import org.omg.PortableInterceptor.ACTIVE;
import org.omg.PortableInterceptor.HOLDING;
import org.omg.PortableInterceptor.INACTIVE;
import org.omg.PortableInterceptor.NON_EXISTENT;

import com.sun.corba.se.spi.protocol.PIHandler;

import com.sun.corba.se.impl.logging.POASystemException;

import com.sun.corba.se.impl.orbutil.ORBUtility;

/**
 * POAManagerImpl is the implementation of the POAManager interface.
 * Its public methods are activate(), hold_requests(), discard_requests()
 * and deactivate().
 */

public class POAManagerImpl extends org.omg.CORBA.LocalObject implements
        POAManager {
    private final POAFactory factory;  // factory which contains global state
    // for all POAManagers
    private PIHandler pihandler;       // for adapterManagerStateChanged
    private State state;                // current state of this POAManager
    private Set poas = new HashSet(4); // all poas controlled by this POAManager
    private int nInvocations = 0;         // Number of invocations in progress
    private int nWaiters = 0;             // Number of threads waiting for
    // invocations to complete
    private int myId = 0;              // This POAManager's ID
    private boolean debug;
    private boolean explicitStateChange; // initially false, set true as soon as
    // one of activate, hold_request,
    // discard_request, or deactivate is called.

    private String stateToString(State state) {
        switch (state.value()) {
            case State._HOLDING:
                return "State[HOLDING]";
            case State._ACTIVE:
                return "State[ACTIVE]";
            case State._DISCARDING:
                return "State[DISCARDING]";
            case State._INACTIVE:
                return "State[INACTIVE]";
        }

        return "State[UNKNOWN]";
    }

    public String toString() {
        return "POAManagerImpl[myId=" + myId +
                " state=" + stateToString(state) +
                " nInvocations=" + nInvocations +
                " nWaiters=" + nWaiters + "]";
    }

    POAFactory getFactory() {
        return factory;
    }

    PIHandler getPIHandler() {
        return pihandler;
    }

    private void countedWait() {
        try {
            if (debug) {
                ORBUtility.dprint(this, "Calling countedWait on POAManager " +
                        this + " nWaiters=" + nWaiters);
            }

            nWaiters++;
            wait();
        } catch (java.lang.InterruptedException ex) {
            // NOP
        } finally {
            nWaiters--;

            if (debug) {
                ORBUtility.dprint(this, "Exiting countedWait on POAManager " +
                        this + " nWaiters=" + nWaiters);
            }
        }
    }

    private void notifyWaiters() {
        if (debug) {
            ORBUtility.dprint(this, "Calling notifyWaiters on POAManager " +
                    this + " nWaiters=" + nWaiters);
        }

        if (nWaiters > 0)
            notifyAll();
    }

    public int getManagerId() {
        return myId;
    }

    POAManagerImpl(POAFactory factory, PIHandler pihandler) {
        this.factory = factory;
        factory.addPoaManager(this);
        this.pihandler = pihandler;
        myId = factory.newPOAManagerId();
        state = State.HOLDING;
        debug = factory.getORB().poaDebugFlag;
        explicitStateChange = false;

        if (debug) {
            ORBUtility.dprint(this, "Creating POAManagerImpl " + this);
        }
    }

    synchronized void addPOA(POA poa) {
        // XXX This is probably not the correct error
        if (state.value() == State._INACTIVE) {
            POASystemException wrapper = factory.getWrapper();
            throw wrapper.addPoaInactive(CompletionStatus.COMPLETED_NO);
        }

        poas.add(poa);
    }

    synchronized void removePOA(POA poa) {
        poas.remove(poa);
        if (poas.isEmpty()) {
            factory.removePoaManager(this);
        }
    }

    public short getORTState() {
        switch (state.value()) {
            case State._HOLDING:
                return HOLDING.value;
            case State._ACTIVE:
                return ACTIVE.value;
            case State._INACTIVE:
                return INACTIVE.value;
            case State._DISCARDING:
                return DISCARDING.value;
            default:
                return NON_EXISTENT.value;
        }
    }

/****************************************************************************
 * The following four public methods are used to change the POAManager's state.
 *
 * A note on the design of synchronization code:
 * There are 4 places where a thread would need to wait for a condition:
 *      - in hold_requests, discard_requests, deactivate, enter
 * There are 5 places where a thread notifies a condition:
 *      - in activate, hold_requests, discard_requests, deactivate, exit
 *
 * Since each notify needs to awaken waiters in several of the 4 places,
 * and since wait() in Java has the nice property of releasing the lock
 * on its monitor before sleeping, it seemed simplest to have just one
 * monitor object: "this". Thus all notifies will awaken all waiters.
 * On waking up, each waiter verifies that the condition it was waiting
 * for is satisfied, otherwise it goes back into a wait().
 *
 ****************************************************************************/

    /**
     * <code>activate</code>
     * <b>Spec: pages 3-14 thru 3-18</b>
     */
    public synchronized void activate()
            throws org.omg.PortableServer.POAManagerPackage.AdapterInactive {
        explicitStateChange = true;

        if (debug) {
            ORBUtility.dprint(this,
                    "Calling activate on POAManager " + this);
        }

        try {
            if (state.value() == State._INACTIVE)
                throw new org.omg.PortableServer.POAManagerPackage.AdapterInactive();

            // set the state to ACTIVE
            state = State.ACTIVE;

            pihandler.adapterManagerStateChanged(myId, getORTState());

            // Notify any invocations that were waiting because the previous
            // state was HOLDING, as well as notify any threads that were waiting
            // inside hold_requests() or discard_requests().
            notifyWaiters();
        } finally {
            if (debug) {
                ORBUtility.dprint(this,
                        "Exiting activate on POAManager " + this);
            }
        }
    }

    /**
     * <code>hold_requests</code>
     * <b>Spec: pages 3-14 thru 3-18</b>
     */
    public synchronized void hold_requests(boolean wait_for_completion)
            throws org.omg.PortableServer.POAManagerPackage.AdapterInactive {
        explicitStateChange = true;

        if (debug) {
            ORBUtility.dprint(this,
                    "Calling hold_requests on POAManager " + this);
        }

        try {
            if (state.value() == State._INACTIVE)
                throw new org.omg.PortableServer.POAManagerPackage.AdapterInactive();
            // set the state to HOLDING
            state = State.HOLDING;

            pihandler.adapterManagerStateChanged(myId, getORTState());

            // Notify any threads that were waiting in the wait() inside
            // discard_requests. This will cause discard_requests to return
            // (which is in conformance with the spec).
            notifyWaiters();

            if (wait_for_completion) {
                while (state.value() == State._HOLDING && nInvocations > 0) {
                    countedWait();
                }
            }
        } finally {
            if (debug) {
                ORBUtility.dprint(this,
                        "Exiting hold_requests on POAManager " + this);
            }
        }
    }

    /**
     * <code>discard_requests</code>
     * <b>Spec: pages 3-14 thru 3-18</b>
     */
    public synchronized void discard_requests(boolean wait_for_completion)
            throws org.omg.PortableServer.POAManagerPackage.AdapterInactive {
        explicitStateChange = true;

        if (debug) {
            ORBUtility.dprint(this,
                    "Calling hold_requests on POAManager " + this);
        }

        try {
            if (state.value() == State._INACTIVE)
                throw new org.omg.PortableServer.POAManagerPackage.AdapterInactive();

            // set the state to DISCARDING
            state = State.DISCARDING;

            pihandler.adapterManagerStateChanged(myId, getORTState());

            // Notify any invocations that were waiting because the previous
            // state was HOLDING. Those invocations will henceforth be rejected with
            // a TRANSIENT exception. Also notify any threads that were waiting
            // inside hold_requests().
            notifyWaiters();

            if (wait_for_completion) {
                while (state.value() == State._DISCARDING && nInvocations > 0) {
                    countedWait();
                }
            }
        } finally {
            if (debug) {
                ORBUtility.dprint(this,
                        "Exiting hold_requests on POAManager " + this);
            }
        }
    }

    /**
     * <code>deactivate</code>
     * <b>Spec: pages 3-14 thru 3-18</b>
     * Note: INACTIVE is a permanent state.
     */

    public void deactivate(boolean etherealize_objects, boolean wait_for_completion)
            throws org.omg.PortableServer.POAManagerPackage.AdapterInactive {
        explicitStateChange = true;

        try {
            synchronized (this) {
                if (debug) {
                    ORBUtility.dprint(this,
                            "Calling deactivate on POAManager " + this);
                }

                if (state.value() == State._INACTIVE)
                    throw new org.omg.PortableServer.POAManagerPackage.AdapterInactive();

                state = State.INACTIVE;

                pihandler.adapterManagerStateChanged(myId, getORTState());

                // Notify any invocations that were waiting because the previous
                // state was HOLDING. Those invocations will then be rejected with
                // an OBJ_ADAPTER exception. Also notify any threads that were waiting
                // inside hold_requests() or discard_requests().
                notifyWaiters();
            }

            POAManagerDeactivator deactivator = new POAManagerDeactivator(this,
                    etherealize_objects, debug);

            if (wait_for_completion)
                deactivator.run();
            else {
                Thread thr = new Thread(deactivator);
                thr.start();
            }
        } finally {
            synchronized (this) {
                if (debug) {
                    ORBUtility.dprint(this,
                            "Exiting deactivate on POAManager " + this);
                }
            }
        }
    }

    private class POAManagerDeactivator implements Runnable {
        private boolean etherealize_objects;
        private POAManagerImpl pmi;
        private boolean debug;

        POAManagerDeactivator(POAManagerImpl pmi, boolean etherealize_objects,
                              boolean debug) {
            this.etherealize_objects = etherealize_objects;
            this.pmi = pmi;
            this.debug = debug;
        }

        public void run() {
            try {
                synchronized (pmi) {
                    if (debug) {
                        ORBUtility.dprint(this,
                                "Calling run with etherealize_objects=" +
                                        etherealize_objects + " pmi=" + pmi);
                    }

                    while (pmi.nInvocations > 0) {
                        countedWait();
                    }
                }

                if (etherealize_objects) {
                    Iterator iterator = null;

                    // Make sure that poas cannot change while we copy it!
                    synchronized (pmi) {
                        if (debug) {
                            ORBUtility.dprint(this,
                                    "run: Preparing to etherealize with pmi=" +
                                            pmi);
                        }

                        iterator = (new HashSet(pmi.poas)).iterator();
                    }

                    while (iterator.hasNext()) {
                        // Each RETAIN+USE_SERVANT_MGR poa
                        // must call etherealize for all its objects
                        ((POAImpl) iterator.next()).etherealizeAll();
                    }

                    synchronized (pmi) {
                        if (debug) {
                            ORBUtility.dprint(this,
                                    "run: removing POAManager and clearing poas " +
                                            "with pmi=" + pmi);
                        }

                        factory.removePoaManager(pmi);
                        poas.clear();
                    }
                }
            } finally {
                if (debug) {
                    synchronized (pmi) {
                        ORBUtility.dprint(this, "Exiting run");
                    }
                }
            }
        }
    }

    /**
     * Added according to the spec CORBA V2.3; this returns the
     * state of the POAManager
     */

    public org.omg.PortableServer.POAManagerPackage.State get_state() {
        return state;
    }

    /****************************************************************************
     * The following methods are used on the invocation path.
     ****************************************************************************/

    // called from POA.find_POA before calling
    // AdapterActivator.unknown_adapter.
    synchronized void checkIfActive() {
        try {
            if (debug) {
                ORBUtility.dprint(this,
                        "Calling checkIfActive for POAManagerImpl " + this);
            }

            checkState();
        } finally {
            if (debug) {
                ORBUtility.dprint(this,
                        "Exiting checkIfActive for POAManagerImpl " + this);
            }
        }
    }

    private void checkState() {
        while (state.value() != State._ACTIVE) {
            switch (state.value()) {
                case State._HOLDING:
                    while (state.value() == State._HOLDING) {
                        countedWait();
                    }
                    break;

                case State._DISCARDING:
                    throw factory.getWrapper().poaDiscarding();

                case State._INACTIVE:
                    throw factory.getWrapper().poaInactive();
            }
        }
    }

    synchronized void enter() {
        try {
            if (debug) {
                ORBUtility.dprint(this,
                        "Calling enter for POAManagerImpl " + this);
            }

            checkState();
            nInvocations++;
        } finally {
            if (debug) {
                ORBUtility.dprint(this,
                        "Exiting enter for POAManagerImpl " + this);
            }
        }
    }

    synchronized void exit() {
        try {
            if (debug) {
                ORBUtility.dprint(this,
                        "Calling exit for POAManagerImpl " + this);
            }

            nInvocations--;

            if (nInvocations == 0) {
                // This notifies any threads that were in the
                // wait_for_completion loop in hold/discard/deactivate().
                notifyWaiters();
            }
        } finally {
            if (debug) {
                ORBUtility.dprint(this,
                        "Exiting exit for POAManagerImpl " + this);
            }
        }
    }

    /**
     * Activate the POAManager if no explicit state change has ever been
     * previously invoked.
     */
    public synchronized void implicitActivation() {
        if (!explicitStateChange)
            try {
                activate();
            } catch (org.omg.PortableServer.POAManagerPackage.AdapterInactive ai) {
                // ignore the exception.
            }
    }
}
